package zx.lrn.notes.multi_threading;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 *
 * @ClassName: ThreadLocalMemLeakDemo
 * @Description: ThreadLocal导致的内存泄漏和解决办法的demo ThreadLocal的工作特点：ThreadLocal本身不存储变量，调用ThreadLocal的set或get时，
 *               实则是先获取到调用处的Thread对象，接着该Thread对象内的ThreadLocalMap属性的存取方法。
 *               所以ThreadLocal只是一个工具类，具体的工作变量是存放在Thread对象内的map中，map的key为ThreadLocal。
 *               由上述知识，线程池内的线程在复用时，线程内的ThreadLocalMap如果没有清空，将对导致内存泄漏问题，下一个线程是可以get到上个线程存储的变量的，
 *               避免泄露的办法就是线程执行完毕前，调用ThreadLocal的remove方法。
 * @Author: zhaoxuan
 * @Date: 2020/3/14 15:56
 **/
public class ThreadLocalMemLeakDemo {
    private static ThreadLocal<LocalDateTime> threadLocal = ThreadLocal.withInitial(LocalDateTime::now);
    private static ExecutorService pool = Executors.newFixedThreadPool(5);

    public static void main(String[] args) throws InterruptedException {
        while (true) {
            // 线程池内线程复用出现泄露问题，线程池容量为5，在第一轮5个线程执行withInitial获取到LocaldateTime后，第二轮5个线程内由于已有该变量，
            // 就不会再次执行withInitial方法，会直接取出第一轮的设置过的LocaldateTime，以后N轮同理。
            IntStream.rangeClosed(1, 5).forEach(i -> pool.submit(() -> {
                System.out.println(threadLocal.get());
                threadLocal.remove(); // 执行remove后，每轮的时间显示就不再重复，说明内存泄漏问题已修复。
            }));
            // 新建线程开销大，但没有泄露问题
            IntStream.rangeClosed(1, 5).forEach(i -> new Thread(() -> System.out.println(threadLocal.get())).start());
            TimeUnit.SECONDS.sleep(3);
        }
    }
}
